import { get } from 'lodash';
import CryptoJS from 'crypto-js';

export function aesMinEncrypt(word) {
  const _word = CryptoJS.enc.Utf8.parse(word),
    _key = CryptoJS.enc.Utf8.parse('ihaierForTodoKey'),
    _iv = CryptoJS.enc.Utf8.parse('ihaierForTodo_Iv');
  const encrypted = CryptoJS.AES.encrypt(_word, _key, {
    iv: _iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  return encrypted.toString();
}

export function assign() {
  const args = [].slice.call(arguments, 0).reverse();
  return Object.assign.apply({}, args);
}

export function getUUID(index) {
  index = typeof index === 'number' && !isNaN(index) && Number.isFinite(index) ? index : 0;
  return (Number(String(Math.random()).slice(2)) + Date.now() + index).toString(36);
}

export function getTotal(list, getter) {
  if (!list || typeof list !== 'object') {
    return 0;
  }
  if (!Array.isArray(list)) {
    list = Object.values(list);
  }
  if (typeof getter === 'string') {
    const path = getter;
    getter = (item) => get(item, path);
  }
  if (typeof getter !== 'function') {
    getter = (item) => item;
  }
  return list.reduce((res, item) => {
    const num = Number(getter(item));
    return res + (Number.isFinite(num) ? num : 0);
  }, 0);
}

export function getPercent(num, total, option) {
  const opt = { toFixed: 1, percent: 100, getter: null };
  option = Object.assign(opt, option);
  /* num: 分子, total: 分母 */
  num = Number(num) || 0;
  if (total && typeof total === 'object') {
    total = getTotal(total, option.getter);
  } else {
    total = Number(total);
  }
  const per = total !== 0 ? (num / total) * option.percent : 0;
  return per === 100 || per === 0 ? per : Number(per.toFixed(option.toFixed));
}

export function getRandomPeopleName() {
  const names =
    '赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵席季麻强贾路娄危';
  function makeWord(max, min) {
    const nameLen = Math.ceil(Math.random() * max + min);
    const name = [];
    for (let i = 0; i < nameLen; i++) {
      name.push(names[Math.round(Math.random() * names.length - 1)]);
    }
    return name.join('');
  }
  return Math.random() > 0.65 ? makeWord(4, 1) + '·' + makeWord(3, 0) : makeWord(2, 1);
}

/*
 * Array; source：数据源
 * Object; fieldsMap：{替换后字段: 原始字段/getter},字段映射表,支持字符串和函数,例: fieldsMap: { id: "id", key: (data) => data["key"] }
 * String; recursiveKey：递归字段, 字段替换是先执行替换后执行递归,所以如果递归字段需要被替换为新的字段的话这个参数应该传新的字段
 * */
export function replaceFields(source = [], fieldsMap = {}, recursiveKey) {
  if (!source || !source.length) {
    return source;
  }
  if (typeof fieldsMap !== 'object' || !Object.keys(fieldsMap).length) {
    return source;
  }
  if (!recursiveKey || typeof recursiveKey !== 'string') {
    recursiveKey = 'children';
  }
  const fields = Object.keys(fieldsMap);
  const originFields = Object.values(fieldsMap).filter((key) => typeof key === 'string');
  function deep(source, recursiveKey, fieldsMap, fields, originFields) {
    return source.reduce((res, item) => {
      const data = fields.reduce((res, key) => {
        const mapKey = fieldsMap[key];
        return Object.assign(res, {
          [key]: typeof mapKey === 'function' ? mapKey(item) : item[mapKey],
        });
      }, {});
      const restData = Object.keys(item)
        .filter((key) => !originFields.includes(key))
        .reduce((res, key) => {
          return Object.assign(res, { [key]: item[key] });
        }, {});
      const children = recursiveKey in fieldsMap ? data[recursiveKey] : restData[recursiveKey];
      if (children && children.length) {
        data[recursiveKey] = deep(children, recursiveKey, fieldsMap, fields, originFields);
      }
      // console.log(data);
      return [].concat(res, [Object.assign(restData, data)]);
    }, []);
  }
  return deep(source, recursiveKey, fieldsMap, fields, originFields);
}

/*
 * Array; data: 检索数组
 * Object; option: {isTarget, getChildren}
 * Function; isTarget: 判定是否为目标的函数
 * Function; getChildren: 获取向下递归的子节点数组
 * */
export function getDataFromArray(data = [], option) {
  option = option && typeof option === 'object' ? option : {};
  let { isTarget, getChildren } = option;
  if (!Array.isArray(data) || typeof isTarget !== 'function') {
    return;
  }
  if (typeof getChildren !== 'function') {
    getChildren = (item) => item.children;
  }
  for (let i = 0, len = data.length; i < len; i++) {
    const item = data[i];
    const children = getChildren(item);
    if (isTarget(item)) {
      return { data: item, index: [i], parent: [data] };
    }
    if (Array.isArray(children)) {
      const res = getDataFromArray(children, option);
      if (res && res.data) {
        return Object.assign(res, {
          index: [].concat(res.index, [i]),
          parent: [].concat(res.parent, [data]),
        });
      }
    }
  }
}

/*
 * 用于将多维数组展平成一维
 * */
export function flattenDeep(arr, option = {}) {
  if (!arr || !arr.length) return [];
  arr = arr.filter((item) => Boolean(item) || typeof item === 'number');
  const opt = {
    /* 扁平化树结构时用于获取子节点数组 */
    getter: (item) => item.children,
    /*
     * format：钩子函数可接收 (item, parent) 两个参数
     * 在推入结果数组前可以用于处理一下数据，比如增删或格式化一些属性及值
     */
    format: (item) => item,
  };
  const deep = function (parent, arr, option, res = []) {
    arr.forEach((item) => {
      const data = option.getter(item);
      if (!Array.isArray(item)) {
        res.push(option.format(item, parent));
      } else if (data !== item) {
        deep(item, item, option, res);
      }
      if (Array.isArray(data)) {
        deep(item, data, option, res);
      }
    });
    return res;
  };
  return deep(null, arr, Object.assign(opt, option));
}

/*
 * arr: Array,数据源数组
 * keyField: String，以arr数组中每条对象的哪个键的值作为结果对象的键
 * valueField: String，以arr数组中每条对象的哪个键的值作为结果对象的值
 * @return {}
 * */
export function turnArr2Obj(arr = [], { keyField, valueField }) {
  if (!arr || !arr.length) return {};
  const getKey = (key, data) => {
    return typeof key === 'function' ? key(data) : data[key];
  };
  return arr.filter(Boolean).reduce((res, item) => {
    const key = getKey(keyField, item);
    return Object.assign(res, {
      [key]: valueField ? item[valueField] : item,
    });
  }, {});
}

export function getReplacedData(dataSource, fieldsMap) {
  if (typeof dataSource !== 'object' || !dataSource) {
    dataSource = {};
  }
  if (typeof fieldsMap !== 'object' || !fieldsMap) {
    fieldsMap = {};
  }
  return Object.keys(fieldsMap).reduce((res, key) => {
    return Object.assign(res, { [key]: dataSource[fieldsMap[key]] });
  }, {});
}

export function parseStr(data, split) {
  if (typeof split !== 'string' || !split) {
    split = ',';
  }
  if (!data || typeof data !== 'string') return [];
  return data.split(split).filter(Boolean);
}

export function parseJson(json, defaultValue) {
  if (typeof defaultValue !== 'object' || !defaultValue) {
    throw new Error('defaultValue is required for parseJson');
  }
  if (typeof json !== 'string') {
    return json;
  }
  const getType = (data) => Object.prototype.toString.call(data).slice(8, -1);
  try {
    // console.log(json);
    json = JSON.parse(json);
    // console.log(json);
    return getType(json) === getType(defaultValue) ? json : defaultValue;
  } catch (e) {
    // console.log("parseJson error: ", json);
    return defaultValue;
  }
}

export const getValidValue = (field, sources = [], predicate) => {
  if (!field || !sources || !sources.length) return;
  if (typeof predicate !== 'function') {
    predicate = (v) => {
      return v !== undefined && v !== null && v !== '' && (typeof v === 'number' ? Number.isFinite(v) : true);
    };
  }
  for (let i = 0, len = sources.length; i < len; i++) {
    const val = get(sources[i], field);
    if (predicate(val)) return val;
  }
};

export function mergeValidData(target, sources, predicate) {
  if (typeof target !== 'object' || !target || !sources || !sources.length) {
    return;
  }
  const getType = (data) => Object.prototype.toString.call(data).slice(8, -1);
  const typeEnum = { Object: {}, Array: [] };

  return Object.keys(target).reduce((res, key) => {
    const sourceVal = getValidValue(key, sources, predicate);
    return Object.assign(res, {
      [key]: sourceVal !== undefined ? sourceVal : target[key],
    });
  }, typeEnum[getType(target)] || typeEnum.Object);
}
